package lumis.portal.serialization;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import lumis.portal.PortalException;
import lumis.portal.authentication.SessionConfig;
import lumis.portal.channel.serialization.ChannelSerializationMessageConfig;
import lumis.portal.group.serialization.GroupSerializationMessageConfig;
import lumis.portal.manager.ManagerFactory;
import lumis.portal.manager.ManagerFactoryInternal;
import lumis.portal.progress.ProgressConfig.Type;
import lumis.portal.serialization.persistency.IDirectory;
import lumis.portal.service.serialization.ServiceSerializationMessageConfig;
import lumis.portal.serviceinstance.serialization.ServiceInstanceSerializationContext;
import lumis.portal.serviceinstance.serialization.ServiceInstanceSerializationMessageConfig;
import lumis.portal.serviceinterface.serialization.ServiceInterfaceSerializationMessageConfig;
import lumis.portal.serviceinterfaceinstance.serialization.ServiceInterfaceInstanceSerializationMessageConfig;
import lumis.portal.user.serialization.UserSerializationMessageConfig;
import lumis.util.ITransaction;
import lumis.util.XmlUtil;

import org.w3c.dom.Document;

/**
 * Context class for portal objects serialization 
 *
 * @version $Revision: 12710 $ $Date: 2011-03-18 17:00:16 -0300 (Fri, 18 Mar 2011) $
 * @since 4.0.7
 */
@SuppressWarnings("serial")
public class SerializationContext extends BaseSerializationContext
{
	private final Set<String> referencedServiceInterfaceStyleIds = new HashSet<String>();
	private final Set<String> referencedCssIds = new HashSet<String>();
	//private final Set<String> referencedWorkflowIds = new HashSet<String>();
	private final Map<String, Set<String>> referencedServiceInterfaceIds = new HashMap<String, Set<String>>();
	private ExternalDependencyLocalizer externalDependencyLocalizer;
	private SerializationSettings externalDependencySerializationSettings;
	private static XStreamUtil externalDependenciesXStreamUtil = null;
	private final List<Dependency> externalDependencies = new ArrayList<Dependency>()
	{

		@Override
		public boolean add(Dependency dependency)
		{
			boolean added = super.add(dependency);

			try
			{
				externalDependencyLocalizer.localizeMessage(dependency);
				if (showExternalDependencyWarnings && dependency.isCauseWarning())
				{
					if(externalDependenciesXStreamUtil == null)
						externalDependenciesXStreamUtil = new XStreamUtil(externalDependencySerializationSettings);
						
					String dependencyString = serializationManager2.serializeObjectToString(dependency, "dependency", externalDependenciesXStreamUtil);
					Document dependencyDoc = XmlUtil.getDocument(dependencyString);
					sendProgressNotification(dependencyDoc, "lumis/portal/serialization/style/ExternalDependency.xsl", Type.WARNING);
				}
			}
			catch (PortalException e)
			{
				throw new RuntimeException("Unexpected exception while adding external dependency", e);
			}
			
			return added;
		}
		
	};
	private final Map<String, Dependency> externalServiceInstanceDependencies = new HashMap<String, Dependency>();
	private final Set<String> channelIds = new HashSet<String>();
	private final ServiceInstanceSerializationContext serviceInstanceSerializationContext;
	private boolean showExternalDependencyWarnings = true;
	private ISerializationManager2 serializationManager2 = ManagerFactoryInternal.getSerializationManager2();
	private boolean includeUserMembership = true;
	
	public SerializationContext(SessionConfig sessionConfig, ITransaction transaction, String rootChannelId, IDirectory directory, boolean progressEnabled, boolean includeContent) throws PortalException
	{
		super(sessionConfig, transaction, rootChannelId, directory, progressEnabled, includeContent);
		this.serviceInstanceSerializationContext = new ServiceInstanceSerializationContext(this);
		externalDependencyLocalizer = new ExternalDependencyLocalizer(sessionConfig, transaction);		
		externalDependencyLocalizer.setTransaction(transaction);
		externalDependencySerializationSettings = getExternalDependenciesSerializationSettings();
		channelIds.addAll(ManagerFactory.getChannelManager().getChannelTreeIds(rootChannelId, transaction));
	}

	public Set<String> getReferencedServiceInterfaceStyleIds()
	{
		return referencedServiceInterfaceStyleIds;
	}

/*	public Set<String> getReferencedWorkflowIds()
	{
		return referencedWorkflowIds;
	}
*/
	public Set<String> getReferencedCssIds()
	{
		return referencedCssIds;
	}

	public Map<String, Set<String>> getReferencedServiceInterfaceIds()
	{
		return referencedServiceInterfaceIds;
	}

	public void addReferencedServiceInterface(String serviceId, String serviceInterfaceId)
	{
		Set<String> interfaceIds = referencedServiceInterfaceIds.get(serviceId);
		if (interfaceIds == null)
		{
			interfaceIds = new HashSet<String>();
			referencedServiceInterfaceIds.put(serviceId, interfaceIds);
		}
		interfaceIds.add(serviceInterfaceId);
	}
	
	public List<Dependency> getExternalDependencies()
	{
		return externalDependencies;
	}

	public Map<String, Dependency> getExternalServiceInstanceDependencies()
	{
		return externalServiceInstanceDependencies;
	}

	public Set<String> getChannelIds()
	{
		return channelIds;
	}

	public ServiceInstanceSerializationContext getServiceInstanceSerializationContext()
	{
		return serviceInstanceSerializationContext;
	}

	public boolean getShowExternalDependencyWarnings()
	{
		return showExternalDependencyWarnings;
	}

	public void setShowExternalDependencyWarnings(boolean showExternalDependencyWarnings)
	{
		this.showExternalDependencyWarnings = showExternalDependencyWarnings;
	}
	
	private SerializationSettings getExternalDependenciesSerializationSettings()
	{
		SerializationSettings serializationSettings = new SerializationSettings();
		serializationSettings.getClassAliases().put(Dependency.class, "dependency");
		serializationSettings.getClassAliases().put(SerializationMessageConfigNode.class, "node");
		serializationSettings.getClassAliases().put(ChannelSerializationMessageConfig.class, "channel");
		serializationSettings.getClassAliases().put(ServiceSerializationMessageConfig.class, "service");
		serializationSettings.getClassAliases().put(ServiceInstanceSerializationMessageConfig.class, "serviceInstance");
		serializationSettings.getClassAliases().put(ServiceInterfaceSerializationMessageConfig.class, "serviceInterface");
		serializationSettings.getClassAliases().put(ServiceInterfaceInstanceSerializationMessageConfig.class, "serviceInterfaceInstance");
		serializationSettings.getOmittedCollectionsForClass(ChannelSerializationMessageConfig.class).add(new SerializationSettings.CollectionAliasSettings("names", "name", String.class));
		serializationSettings.getFieldAliasesForClass(SerializationMessageConfigNode.class).put("serializationMessageConfig", "config");
		serializationSettings.getTransientFieldsForClass(UserSerializationMessageConfig.class).add("globalPrincipal");
		serializationSettings.getTransientFieldsForClass(GroupSerializationMessageConfig.class).add("globalPrincipal");
		return serializationSettings;
	}

	@Override
	public void setTransaction(ITransaction transaction)
	{
		super.setTransaction(transaction);
		if(externalDependencyLocalizer != null)
			externalDependencyLocalizer.setTransaction(transaction);
	}
	
	/**
	 * Sets whether the serialization will serialize the structure recursively or not.
	 * See {@link #isShallowSerialization()} for more details on recursion implications.
	 *
	 * @param shallowSerialization the shallow serialization value.
	 * @since 5.0.0
	 */
	public void setShallowSerialization(boolean shallowSerialization)
	{
		super.setShallowSerialization(shallowSerialization);
	}

	/**
	 * Indicates if information about users being member of a group being
	 * exported is to be included.
	 * <p>
	 * Note that this refers only to members that are users, this does
	 * not affect members that are groups.
	 * @return if true, information about users being member of a group being
	 * exported is to be included; if false such information must not be 
	 * included.
	 * @since 4.2.1
	 */
	public boolean getIncludeUserMembership()
	{
		return includeUserMembership;
	}

	/**
	 * Sets if information about users being member of a group being
	 * exported is to be included.
	 * <p>
	 * Note that this refers only to members that are users, this does
	 * not affect members that are groups.
	 * @param include true to be included, false otherwise.
	 * @since 4.2.1
	 */
	public void setIncludeUserMembership(boolean include)
	{
		this.includeUserMembership = include;
	}
}
